home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Development Platforms / LISP Related / U. Mass AI & LISP Tools / INTERFACE / Hyper-Display.lisp < prev   
Encoding:
Text File  |  1990-06-24  |  34.4 KB  |  728 lines  |  [TEXT/CCL ]

  1. ; (c) Copyright 1990 by University of Massachusetts. All rights reserved.
  2. ; This software was conceived, designed, and written by Dan Suthers 
  3. ; while supported by the National Science Foundation under grant number
  4. ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
  5. ; CA.  Partial support was also received from the Office of Naval Research
  6. ; under a University Research Initiative Grant, contract N00014-86-K-0764.
  7. ; Mr. Suthers created this software under his own initiative while in an 
  8. ; academic relationship with the University of Massachusetts.  The above
  9. ; copyright notice was a condition placed by University lawyers on approval
  10. ; of distribution of this software by Apple Computer, and is not meant to
  11. ; imply that this software was created in an employment or "work for hire"
  12. ; relationship between the University and Mr. Suthers.
  13. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  14. ; File:         Hyper-Display.lisp
  15. ; Author:       Dan Suthers
  16. ; Created:      15-Apr-89 14:07:15
  17. ; Modified:     24-Jun-90 02:11:26 (Dan Suthers)
  18. ; Language:     Common Lisp
  19. ; Package:      USER
  20. ;
  21. ; Description:  A Hyper-text like display and browsing interface. The 
  22. ; application provides a hierarchically structured representation of 
  23. ; some text, and command functions.  Regions of text (possibly nested) 
  24. ; have structures with pointers to application data structures associated 
  25. ; with them.  Hyper-Display displays the text in a specialized Fred window.
  26. ; When the user selects a range of the text with a mouse, Hyper-Display is 
  27. ; able to determine the smallest structure in the hierarchy which encloses 
  28. ; that region.  When the user initiates a query on this selected region of 
  29. ; text, the command function is called on the structure representing the 
  30. ; selected region.  For example, a command could be a request to define a
  31. ; term, and the function could replace the text in the window with a 
  32. ; definition, preserving a pointer to the previous hyper-structure, so
  33. ; that another command can return to the original text if desired.
  34. ;
  35. ; (c) Copyright 1989, by Daniel D. Suthers
  36. ;                        Department of Computer and Information Science
  37. ;                        University of Massachusetts
  38. ;                        Amherst, Massachusetts 01003
  39. ; All rights reserved.
  40. ;
  41. ; This software was conceived, designed, and written by Dan Suthers 
  42. ; while supported by the National Science Foundation under grant number
  43. ; MDR 8751362, and by a fellowship from Apple Computer, Inc., Cupertino,
  44. ; CA.  Partial support was also received from the Office of Naval Research
  45. ; under a University Research Initiative Grant, contract N00014-86-K-0764.
  46. ; I wish to acknowledge the generous support of Beverly Woolf, who obtained 
  47. ; the above grants and encouraged me to pursue my own research interests in
  48. ; her lab.  This work would not have been possible without the resources and
  49. ; stimulating environment of the Computer and Information Science department.
  50. ;
  51. ; Permission to use, modify, and distribute this software is granted subject 
  52. ; to the following restrictions and understandings:
  53. ; 1. The file header, including this notice, shall be retained, and may be
  54. ;    extended to include documentation of modifications to the software.
  55. ; 2. This material is for nonprofit educational and research purposes only.
  56. ;    Users are requested, but not required, to inform Mr. Suthers of any 
  57. ;    noteworthy uses of this software.
  58. ; 3. Mr. Suthers and the University of Massachusetts make no warantee or
  59. ;    representation that the operation of this software will be error free,
  60. ;    and are under no obligation to provide any services.
  61. ; 4. Any user of such software agrees to indemnify and hold harmless Mr.
  62. ;    Suthers and the University of Massachusetts from all claims arising 
  63. ;    out of the use or misuse of this software, or arising out of any 
  64. ;    accident, injury, or damage whatsoever, and from all costs, counsel
  65. ;    fees, and liabilities incurred in or about any such claim, action, or
  66. ;    proceeding brought thereon.
  67. ; 5. All materials and reports developed as a consequence of the use of 
  68. ;    this software shall duly acknowledge such use, in accordance with
  69. ;    the usual standards of acknowledging credit in academic research.
  70. ;
  71. ; Status:       A prototype version, but working fine.
  72. ;
  73. ; Tested:       Macintosh II Coral/Allegro 16-Apr-89 15:49:59 Dan Suthers
  74. ;
  75. ; Changes:      
  76. ;   24-Jun-90 DS Temporary hack around table search problem; see @ BUG.
  77. ;
  78. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  79. ;
  80. ;                      DOCUMENTATION FOR EXTERNAL USERS
  81. ;
  82. ; The User's View:
  83. ;
  84. ; The user sees something looking like a Fred window with text displayed 
  85. ; in it.  The user may move and resize the window, use the scroll bars,
  86. ; and select regions with the mouse.  The contents of the window cannot
  87. ; be altered: a beep and a pop-up message window responds to attempts to
  88. ; do so (or to undefined keystrokes).  Only a small number of keys, in
  89. ; particular including the Help key, may be meaningfully used.
  90. ;
  91. ; A Hyper-Display window may have zero or more commands associated with it
  92. ; for operating on selected regions. (It is up to the application whether
  93. ; these commands expand the region into a new hypertext structure to be
  94. ; displayed, or performs some other operation.) The user can always access 
  95. ; any of these commands by using the Help key.  If there is no selected 
  96. ; region, this key pops up a general description of how to use HyperDisplay.
  97. ; If there is a selection, Help puts up a menu of defined commands.  Some 
  98. ; commands may have other keystrokes associated with them, for direct access 
  99. ; without using the menu.  The activated command is applied to the smallest 
  100. ; region enclosing the currently selected text, or to the next region if
  101. ; there is no selection.
  102. ;
  103. ; The Client Program's View:
  104. ;
  105. ; To create a Hyper-Display, the application must give a hyper-structure and
  106. ; a set of commands to the function CREATE-HYPER-DISPLAY.  
  107. ;
  108. ; A Hyper-structure is a Common Lisp structure. (The program interface is
  109. ; designed so that client programs may be written in pure Common Lisp, 
  110. ; not being forced to deal with the object language used to implement this.)
  111. ; It has three client-accessible fields:
  112. ;   Text-Specs: a list of strings and/or recursive hyper-structures.  
  113. ;     The text string to be displayed in the window is constructed by 
  114. ;     traversing the top level text-specs, concatenating strings and 
  115. ;     recursively converting embedded hyper-structures into text.
  116. ;   Parent: backpointer to the hyper-structure whose text-specs this
  117. ;     structure is embedded in.  Note: the present code makes no use
  118. ;     of this slot, and does not attempt to guarantee that its contents
  119. ;     are correct.  Its maintenance is up to the application.
  120. ;   Object: a pointer to the application-specific data structure the text
  121. ;     corresponds to. This is presumably used by the command functions to
  122. ;     respond to queries, e.g. if the object is a node in a text plan, 
  123. ;     then a "define" query may be responded to by displaying text
  124. ;     defining the associated concept.
  125. ;
  126. ; The application specifies the commands as a list of tuples:
  127. ;   ( (<key> <description> <method>) *)
  128. ; where:
  129. ;   <key> is a character specifying the command key which invokes the 
  130. ;     command (#\^E for Help if the command is only available on the menu)
  131. ;   <description> is a short string describing the command (appropriate
  132. ;     for menu display)
  133. ;   <method> is a lambda form or function of two arguments which performs 
  134. ;      the desired operation.  The arguments are the most specific hyper-
  135. ;      structure which includes the text the user selected, and the window
  136. ;      object containing the text.
  137. ;
  138. ; External Functions:
  139. ; CREATE-HYPER-DISPLAY is given the hyper-structure and command list, and
  140. ;   creates and returns a window object, which should be retained for 
  141. ;   other functions operating on the window. 
  142. ; DISPLAY-HYPER-STRUCTURE sets a window's hyper-structure to one specified,
  143. ;   and sets the window's buffer to the resulting text, so that is will be
  144. ;   displayed if and when the window is visible. 
  145. ;
  146. ; MAKE-HYPER-STRUCTURE, HYPER-STRUCTURE-TEXT-SPECS, HYPER-STRUCTURE-PARENT,
  147. ; HYPER-STRUCTURE-OBJECT, COPY-HYPER-STRUCTURE, and HYPER-STRUCTURE-P: 
  148. ;   Defined by defstruct.
  149. ;
  150. ; HYPER-STRUCTURE-TEXT: returns a string constructed from recursive 
  151. ;   traversal of the hyper-structure-specs.  Useful for debugging.
  152. ;
  153. ; The application is responsible for other features such as maintaining
  154. ; context stacks of hyper-structures for push/pop facilities, etc. 
  155. ;
  156. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  157. ;
  158. ;                           Pending Improvements:
  159. ;
  160. ; Fix mouse click bug so window-mouse-up-event-handler called inside text
  161. ; region (not just mini-buffer). (However, there may be an advantage to
  162. ; leaving it this way: commands that want to know *exactly* what is 
  163. ; selected can operate.)
  164. ; Rewrite to not construct a string of the entire buffer's text.  Instead,
  165. ; write the text into the buffer on the fly.  Then I don't need to risk
  166. ; modifying strings with trim-right-margin, and it is faster.
  167. ;
  168. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  169.  
  170. (in-package :HYPER-DISPLAY)
  171.  
  172. (use-package :CCL)
  173.  
  174. (export '(
  175.           *Hyper-Display-window*
  176.  
  177.           create-hyper-display
  178.           display-hyper-structure
  179.  
  180.           hyper-structure
  181.           copy-hyper-structure
  182.           hyper-structure-object
  183.           hyper-structure-p
  184.           hyper-structure-parent
  185.           hyper-structure-text
  186.           hyper-structure-text-specs
  187.           make-hyper-structure
  188.  
  189.           ))
  190.  
  191. (require :DIALOGUE)
  192.  
  193. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  194. ;;;
  195. ;;;                             PARAMETERS
  196. ;;;
  197. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  198.  
  199. (eval-when (compile eval)
  200.  
  201.   (defconstant *DEFAULT-RIGHT-MARGIN* 75)
  202.  
  203.   (defconstant *DEFAULT-MINI-BUFFER-UNSELECTED-MESSAGE*
  204.     "Use Help key for instructions.")
  205.  
  206.   (defconstant *DEFAULT-MINI-BUFFER-SELECTED-MESSAGE*
  207.     "Use Help key for menu of commands.")
  208.  
  209.   (defconstant *DEFAULT-MENU-MESSAGE*
  210.     "Choose an action to apply to your selection:")
  211.  
  212.   (defconstant *READ-ONLY-MESSAGE*
  213.     "Hyper-Display windows are Read-Only (the text can't
  214. be modified).  Push the Help key for instructions.")
  215.  
  216.   (defconstant *DEFAULT-GENERAL-INSTRUCTIONS*
  217.     "You are using a HyperDisplay window, which allows you to perform
  218. certain actions on the displayed text.  To use the display, first
  219. use the mouse to select the text you have a question about or want
  220. to perform an action on.  (Do this the usual way: holding down the
  221. mouse and moving it across the text.  You don't have to select all
  222. of the region exactly, you only have to select most of it and the
  223. HyperDisplay will use the smallest region enclosing what you have
  224. selected.)  Then press the Help key.  You will be shown a menu of 
  225. actions you can take: select the one desired.  Some actions can be 
  226. invoked directly by keystrokes: there is an option on the Help menu 
  227. to find out what these keystrokes are.  The contents of this window 
  228. cannot be modified, except by the defined actions.")
  229.  
  230.   ) ; eval-when
  231.  
  232. (defconstant *HYPER-DISPLAY-PACKAGE* (find-package :hyper-display))
  233.  
  234. (defvar *WINDOW-NAME-COUNTER* 0)
  235.  
  236. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  237. ;;;
  238. ;;;                               DATA TYPES
  239. ;;;
  240. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  241. ;;; Hyper-Structures
  242.  
  243. (defstruct HYPER-STRUCTURE
  244.   (TEXT-SPECS nil    :type list)
  245.   (PARENT     nil    :type hyper-structure)
  246.   (OBJECT     nil)
  247.   (SELECTION-START 0 :type fixnum)  ; Boundaries of buffer substring within
  248.   (SELECTION-END   0 :type fixnum)) ; which the structure is displayed.
  249.  
  250. ;;; This makes the structure appear to have another slot: the expansion of
  251. ;;; the text-specs.  Not used by the present code, but very useful.
  252.  
  253. (defun HYPER-STRUCTURE-TEXT (hs)
  254.   "hyper-structure-text <hyper-structure>
  255.   Returns the string which would be displayed for the given structure."
  256.   (declare (type hyper-structure hs) 
  257.            (optimize (safety 1) (space 2) (speed 3)))
  258.   ;; Reduce list of strings to one string.
  259.   (reduce #'(lambda (s1 s2)
  260.               (declare (string s1 s2))
  261.               (concatenate 'string s1 s2))
  262.           ;; Get a list of strings (all recursive specs expanded).
  263.           (mapcar #'(lambda (spec)
  264.                       (if (stringp spec) 
  265.                         spec
  266.                         (hyper-structure-text spec)))
  267.                   (hyper-structure-text-specs hs))
  268.           :initial-value ""))
  269.  
  270. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  271. ;;; Region-Tables
  272. ;;;
  273. ;;; Region-tables are nested lists which enable a fast mapping from the
  274. ;;; start and end of a selected region of text in the buffer to the smallest
  275. ;;; underlying hyper-structure which includes the selected region.  Padding
  276. ;;; of one character is allowed on each side of the region's actual text to
  277. ;;; allow for sloppy mouse manipulation.  This should not produce ambiguity
  278. ;;; as long as regions are delimited by spaces, etc. An example of a region
  279. ;;; list for the string "This is a test." is given below -- assume that the
  280. ;;; symbols are really hyper-structures:
  281. ;;;   ((9 . ((14 . test)))           ; region "test" occurs in columns 10-13
  282. ;;;    (7 . ((9 . a) (14 . a-test))) ; region "a" in 8-8; "a test" in 8-13
  283. ;;;    (4 . ((7 . is)))              ; region "is" in 5-6
  284. ;;;    (-1 . ((4 . This))))          ; region "This" in 0-3
  285. ;;; Note that this includes nested regions for illustrative purposes.
  286. ;;; The structure of the table is as follows.  The outer list indexes in
  287. ;;; decreasing order the start columns of the regions.  The appropriate
  288. ;;; sub-list is identified by finding the largest sublist index which is
  289. ;;; smaller than the start of the range the user selected.  Then, since 
  290. ;;; several regions may start at the same column, a similar search is 
  291. ;;; conducted in the sublist.  These entries are in increasing order of
  292. ;;; the end column of the region, and the smallest index which is larger
  293. ;;; than the end column of the selected region is chosen.  The CDR of this
  294. ;;; entry is the hyper-structure to be used.
  295.  
  296. ;;; These are needed to build the lists.
  297. (eval-when (compile eval)
  298.  
  299.   (defmacro INSERT-CONS-INCREASING (the-cons the-list)
  300.     `(setf ,the-list (merge 'list (list ,the-cons) ,the-list 
  301.                             #'(lambda (cons1 cons2)
  302.                                 (declare (cons cons1 cons2))
  303.                                 (< (car cons1) (car cons2))))))
  304.  
  305.   (defmacro INSERT-CONS-DECREASING (the-cons the-list)
  306.     `(setf ,the-list (merge 'list (list ,the-cons) ,the-list 
  307.                             #'(lambda (cons1 cons2)
  308.                                 (declare (cons cons1 cons2))
  309.                                 (> (car cons1) (car cons2))))))
  310.   ) ; eval-when
  311.  
  312. ;;; The fundamental construction operation.  It is assumed that the start
  313. ;;; and end values are already adjusted for the padding of 1 column.
  314.  
  315. (defun ADD-REGION (region-start region-end region-structure region-list)
  316.   (declare (fixnum region-start region-end) (list region-list)
  317.            (type hyper-structure region-structure)
  318.            (optimize (safety 1) (space 2) (speed 3)))
  319.   (let ((existing-sublist (assoc region-start region-list))
  320.         (region-entry (cons region-end region-structure)))
  321.     (declare (list existing-sublist) (cons region-entry))
  322.     (if existing-sublist
  323.       (insert-cons-increasing region-entry (cdr existing-sublist))
  324.       (insert-cons-decreasing (cons region-start (list region-entry)) 
  325.                               region-list))
  326.     region-list))
  327.  
  328. ;;; Search function for finding the appropriate region given start and end.
  329. ;;; @ BUG: If the start is in one sub-structure and the end in another sub
  330. ;;; structure, this returns nil even though there may be an enclosing super
  331. ;;; structure. Reason: it goes into the subentry indexed by the first sub's
  332. ;;; start position, but there is then no entry >= end in that subtable
  333. ;;; because the end is beyond its range.  On this sort of failure, need to
  334. ;;; back down one entry in the table and try again (repeatedly).  I "fixed"
  335. ;;; this with the hack of decrementing range-start and calling the search
  336. ;;; function recursively, repeating until range-start reaches 0 or the call
  337. ;;; returns a non-null region.  THE REAL SOLUTION is to have each list in 
  338. ;;; the table continued by the previous list -- that is, to hack the list
  339. ;;; structure at creation time so the search-region-table algorithm WITHOUT
  340. ;;; the recursive call hack works correctly by following a pointer to the
  341. ;;; previous list without knowing it. 
  342.  
  343. (defun SEARCH-REGION-TABLE (range-start range-end region-table)
  344.   (declare (fixnum range-start range-end) (list region-table)
  345.            (optimize (safety 1) (space 2) (speed 3)))
  346.   (let ((ptr region-table))
  347.     (declare (list ptr))
  348.     ;; Search for appropriate subtable (list): the largest entry <= start.
  349.     (loop 
  350.       (cond ((null ptr) (return))
  351.             ((<= (car (first ptr)) range-start)
  352.              (setq ptr (cdr (first ptr)))
  353.              (return))
  354.             (T (setq ptr (cdr ptr)))))
  355.     ;; Search for appropriate entry in subtable: smallest entry >= end.
  356.     (loop
  357.       (cond ((null ptr) (return))
  358.             ((>= (car (first ptr)) range-end)
  359.              (setq ptr (cdr (first ptr)))
  360.              (return))
  361.             (T (setq ptr (cdr ptr)))))
  362.     (or ptr 
  363.         (if (> range-start 0) ; @ temporary fix, see comment above
  364.           (search-region-table (1- range-start) range-end region-table)))))
  365.  
  366. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  367. ;;; Hyper-Display Windows
  368. ;;;
  369. ;;; These are Fred windows with the following changes:
  370. ;;; * Additional user-definable object-variables:
  371. ;;;   - HYPER-STRUCTURE: the top level structure displayed in the window.
  372. ;;;   - RIGHT-MARGIN: used to format text generated from hyper-structure.
  373. ;;;   - MINI-BUFFER-MESSAGE: displayed in the mini-buffer to tell the 
  374. ;;;     user how to proceed.
  375. ;;;   - MENU-MESSAGE: title for menu of available commands.
  376. ;;; * Additional internal object-variables:
  377. ;;;   - COMMAND-TABLE: hash table constructed from the given command-list.
  378. ;;;   - REGION-TABLE: for identifying selected region; see above.
  379. ;;; * All operations which modify the contents of the buffer are disabled.
  380. ;;; * Specified command keys invoke commands on the selected region.
  381.  
  382. (defobject *HYPER-DISPLAY-WINDOW* *fred-window*)
  383.  
  384. (defobfun (EXIST *Hyper-Display-window*) (init-list)
  385.   (declare (object-variable command-table 
  386.                             hyper-structure 
  387.                             mini-buffer-selected-message
  388.                             mini-buffer-unselected-message
  389.                             general-instructions
  390.                             region-table))
  391.   (let ((hyper-structure 
  392.          (getf init-list :hyper-structure (make-hyper-structure)))
  393.         (command-list (getf init-list :command-list nil))
  394.         (right-margin 
  395.          (getf init-list :right-margin *default-right-margin*))
  396.         (menu-message 
  397.          (getf init-list :menu-message *default-menu-message*))
  398.         (mini-buffer-selected-message 
  399.          (getf init-list :mini-buffer-selected-message 
  400.                *default-mini-buffer-selected-message*))
  401.         (mini-buffer-unselected-message
  402.          (getf init-list :mini-buffer-unselected-message 
  403.                *default-mini-buffer-unselected-message*))
  404.         (general-instructions
  405.          (getf init-list :general-instructions
  406.                *default-general-instructions*)))
  407.     (check-type right-margin fixnum)
  408.     (check-type menu-message string)
  409.     (check-type mini-buffer-selected-message string)
  410.     (check-type mini-buffer-unselected-message string)
  411.     (check-type general-instructions string)
  412.     ;; These must be done first in case :window-show is specified T by user.
  413.     ;; That causes display to occur early: the methods expect these to be "had".
  414.     (have 'hyper-structure hyper-structure)
  415.     (have 'region-table nil)                ; temporary (until text displayed)
  416.     (have 'command-table (make-hash-table)) ; keystrokes to (<string> <function>)
  417.     (have 'right-margin right-margin)
  418.     (have 'menu-message menu-message)
  419.     (have 'mini-buffer-selected-message mini-buffer-selected-message)
  420.     (have 'mini-buffer-unselected-message mini-buffer-unselected-message)
  421.     (have 'general-instructions general-instructions)
  422.     ;; Now it is safe to make the thing exist as a Fred Window.
  423.     (usual-exist
  424.      (init-list-default
  425.       init-list
  426.       :window-title     (format nil "Hyper Display ~A"
  427.                                 (incf *window-name-counter*))
  428.       :package *hyper-display-package*
  429.       ;; Position and size inherited from Fred.
  430.       :window-show      nil
  431.       :window-font     '("monaco" 12)
  432.       :window-type     :document-with-zoom
  433.       :close-box       t))
  434.     ;; Set up the command tables; compute text (put in buffer) and region table.
  435.     (set-command-table command-list)
  436.     (layout-hyper-display)
  437.     (set-mini-buffer mini-buffer-unselected-message)
  438.     ;; Now show it, unless user specified not to.
  439.     (if (getf init-list :window-show t) (window-show))
  440.     ;; Return object created.
  441.     (self)))
  442.  
  443. ;;;------------------------------------------------------------------------
  444. ;;; These are needed to keep the message in the mini-buffer.  They call the
  445. ;;; corresponding usual method (which writes size or position change message
  446. ;;; into the buffer), then rewrite the desired message into the buffer.
  447.  
  448. (defobfun (SET-WINDOW-SIZE *Hyper-Display-window*) (h &optional (v nil))
  449.   (declare (object-variable mini-buffer-selected-message
  450.                             mini-buffer-unselected-message))
  451.   (funcall (ask *fred-window* (symbol-function 'set-window-size)) h v)
  452.   (multiple-value-bind (start end) (selection-range)
  453.     (declare (fixnum start end))
  454.     (if (= start end)
  455.       (set-mini-buffer mini-buffer-unselected-message)
  456.       (set-mini-buffer mini-buffer-selected-message))))
  457.  
  458. (defobfun (SET-WINDOW-POSITION *Hyper-Display-window*) (h &optional (v nil))
  459.   (declare (object-variable mini-buffer-selected-message
  460.                             mini-buffer-unselected-message))
  461.   (funcall (ask *fred-window* (symbol-function 'set-window-position)) h v)
  462.   (multiple-value-bind (start end) (selection-range)
  463.     (declare (fixnum start end))
  464.     (if (= start end)
  465.       (set-mini-buffer mini-buffer-unselected-message)
  466.       (set-mini-buffer mini-buffer-selected-message))))
  467.  
  468. ;;;------------------------------------------------------------------------
  469. ;;; Some standard methods are redefined to prevent modification of the text.
  470.  
  471. (eval-when (compile eval) ; this is used by window-key-event-handler also
  472.   (defmacro INFORM-READ-ONLY ()
  473.     ;; Used to warn the user in response to undefined keystrokes or attempts
  474.     ;; to modify the buffer.
  475.     `(progn
  476.        (ccl:ed-beep)
  477.        (wind:message-dialogue ,*read-only-message*)))
  478.   )
  479.  
  480. (defobfun (CUT *Hyper-Display-window*) () (inform-read-only))
  481. (defobfun (PASTE *Hyper-Display-window*) () (inform-read-only))
  482.  
  483. ;;;------------------------------------------------------------------------
  484. ;;; Methods for building and modifying Hyper-Display data structures.
  485.  
  486. (defobfun (SET-HYPER-STRUCTURE *Hyper-Display-window*) (structure)
  487.   (declare (object-variable hyper-structure region-table))
  488.   (buffer-delete (window-buffer) 
  489.                  :start 0
  490.                  :length (buffer-size (window-buffer)))
  491.   (setq hyper-structure structure)
  492.   (layout-hyper-display)
  493.   structure)
  494.  
  495. (defobfun (SET-COMMAND-TABLE *Hyper-Display-window*) (command-list)
  496.   ;; Command list of form ((<char> <string> <function>)*).
  497.   ;; Hash table associates <char> to (<string> <function>), except
  498.   ;; that <char> of #\^E (Help key) has a list of all known
  499.   ;; (<string> <function>) tuples, regardless of whether recorded
  500.   ;; under a key.
  501.   (declare (object-variable command-table) (list command-list)
  502.            (optimize (safety 1) (space 2) (speed 3)))
  503.   (clrhash command-table)
  504.   ;; Record all commands in order given, for menu specification,
  505.   ;; followed by menu-only command which lists keystrokes for commands.
  506.   (setf (gethash #\^E command-table) 
  507.         (nconc (mapcar #'rest command-list)
  508.                (list `("Show Keystrokes for Commands"
  509.                        (lambda (hs hd) (declare (ignore hs hd))
  510.                                (wind:message-dialogue 
  511.                                 ,(format nil "Keystrokes and Commands:~:{~%~A  ~A~}"
  512.                                          (remove #\^E command-list :key #'first))))))))
  513.   ;; Record those having a key other than the help key under their keys.
  514.   (dolist (command-spec command-list)
  515.     (declare (list command-spec))
  516.     (unless (char= (first command-spec) #\^E)
  517.       (setf (gethash (first command-spec) command-table)
  518.             (rest command-spec)))))
  519.  
  520. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  521. ;;;
  522. ;;;                     MAJOR INTERNAL FUNCTIONALITY
  523. ;;;
  524. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  525.  
  526. (defobfun (LAYOUT-HYPER-DISPLAY *Hyper-Display-Window*) ()
  527.   (declare (object-variable hyper-structure region-table right-margin)
  528.            (list region-table) (fixnum right-margin))
  529.   (multiple-value-bind
  530.     (region-list buffer-string fill-column)
  531.     (compute-regions hyper-structure '() "" 0)
  532.     (declare (list region-list) (string buffer-string) 
  533.              (fixnum fill-column)
  534.              (optimize (safety 1) (space 2) (speed 3)))
  535.     (setf region-table region-list)
  536.     ;; Record start and end of top level structure's selection region.
  537.     (setf (hyper-structure-selection-start hyper-structure) 0)
  538.     (setf (hyper-structure-selection-end   hyper-structure) (1- fill-column))
  539.     ;; Insert the string.  Note this assumes an empty buffer.
  540.     (buffer-insert (window-buffer) 
  541.                    (wind:trim-right-margin buffer-string right-margin) 
  542.                    0)))
  543.  
  544. ;;; Function for constructing the region-tables.
  545. ;;; Returns three values: a constructed region-table (called region-list to
  546. ;;; avoid confusion with an object variable) for the structure, a string 
  547. ;;; containing the text of the structure's region, and the fill-column at
  548. ;;; which concatenation to that string would continue (i.e. the length of
  549. ;;; the string).  Padding around regions is handled here.
  550.  
  551. (defun COMPUTE-REGIONS (structure region-list buffer-string initial-fill-column)
  552.   (declare (type hyper-structure structure) (list region-list)
  553.            (string buffer-string) (fixnum initial-fill-column)
  554.            (optimize (safety 1) (space 2) (speed 3)))
  555.   ;; Iterate down text-specs to construct the tree & text at this level.
  556.   (do ((spec-ptr (hyper-structure-text-specs structure) (cdr spec-ptr))
  557.        (fill-column initial-fill-column))
  558.       ((null spec-ptr) (values region-list buffer-string fill-column))
  559.     (declare (list spec-ptr) (fixumn fill-column))
  560.     (cond 
  561.      ;; Strings are just appended to the buffer, with no region construction.
  562.      ((stringp (first spec-ptr))
  563.       (setq buffer-string (concatenate 'string buffer-string (first spec-ptr)))
  564.       (incf fill-column (length (first spec-ptr))))
  565.      (t
  566.       ;; Subregions handled by building text into buffer recursively, then 
  567.       ;; recording the region in the table after we know where the region ends.
  568.       (let ((subregion-start (max (1- fill-column) 0))) ; save padded beginning
  569.         (declare (fixnum subregion-start))
  570.         (multiple-value-setq
  571.          (region-list buffer-string fill-column)
  572.          (compute-regions (first spec-ptr) region-list buffer-string fill-column))
  573.         ;; Record the range in the region structure itself, and in the table.
  574.         (setf (hyper-structure-selection-start (first spec-ptr)) subregion-start)
  575.         (setf (hyper-structure-selection-end   (first spec-ptr)) fill-column)
  576.         (setq region-list 
  577.               (add-region subregion-start fill-column ; already 1+ the region end
  578.                           (first spec-ptr) region-list)))))))
  579.  
  580. ;;; Redefine low-level keystroke handling to bypass Fred's comtabs and invoke
  581. ;;; our own legal command keys, or notify user if undefined key used.
  582.  
  583. (defobfun (WINDOW-KEY-EVENT-HANDLER *Hyper-Display-window*) (c)
  584.   (declare (object-variable command-table general-instructions menu-message
  585.                             mini-buffer-unselected-message (self))
  586.            (optimize (safety 1) (space 2) (speed 3)))
  587.   (cond 
  588.  
  589.    ((char= c #\^E) ; Help key.
  590.     ;; If there is no selection, put up general instructions.  Otherwise, 
  591.     ;; Put up a menu of actions (documentation strings are in the first
  592.     ;; position) and call the selected function (second position) on the 
  593.     ;; selected sub-structure.  (All commands were stored under #\^E.)
  594.     (multiple-value-bind (start end) (selection-range)
  595.       (declare (fixnum start end))
  596.       (if (= start end)
  597.         (wind:message-dialogue general-instructions)
  598.         (funcall
  599.          (second (assoc (wind:menu-dialogue
  600.                          (mapcar #'first (gethash #\^E command-table))
  601.                          menu-message)
  602.                         (gethash #\^E command-table)))
  603.          (selected-hyper-structure)
  604.          (self)))))
  605.  
  606.    ((member c '(#\ #\ #\ #\ #\  #\ ))
  607.     ;; Let left, right, up, down arrows, page-up, and page-down be 
  608.     ;; interpreted by Fred normally.
  609.     (funcall (ask *fred-window* (symbol-function 'window-key-event-handler)) 
  610.              c (self))
  611.     (set-mini-buffer mini-buffer-unselected-message))
  612.  
  613.     (T
  614.      ;; Other keys only legal if found in command table.
  615.      (let ((command-entry (gethash c command-table)))
  616.        (declare (list command-entry))
  617.        (if command-entry 
  618.          (funcall (second command-entry) (selected-hyper-structure) (self))
  619.          (inform-read-only))))))
  620.  
  621. ;;; Identifying the smallest hyper-structure which encloses selected region
  622. ;;; or follows the cursor.  If none found, it is the whole text.
  623.  
  624. (defobfun (SELECTED-HYPER-STRUCTURE *Hyper-Display-Window*) ()
  625.   (declare (object-variable region-table hyper-structure)
  626.            (optimize (safety 1) (space 2) (speed 3)))
  627.   (multiple-value-bind 
  628.     (start end) (selection-range)
  629.     (declare (fixnum start end))
  630.     (decf end) ; it should point to last used, not the one after.
  631.     (or (search-region-table start end region-table) hyper-structure)))
  632.  
  633. ;;; When the user releases the mouse, the selection is expanded to show
  634. ;;; the user what the selected region is.  BUG in CCL: only activated
  635. ;;; when mouse in the mini-buffer!  Maybe this is OK?
  636.  
  637. (defobfun (WINDOW-MOUSE-UP-EVENT-HANDLER *Hyper-Display-Window*) ()
  638.   (declare (object-variable mini-buffer-selected-message 
  639.                             mini-buffer-unselected-message))
  640.   (let ((hyper-structure (selected-hyper-structure)))
  641.     (set-mark (window-cursor-mark) 
  642.               (hyper-structure-selection-start hyper-structure))
  643.     (set-selection-range
  644.      ;; Special case guards against selecting off end of buffer.
  645.      (if (= (buffer-size (window-buffer))
  646.             (hyper-structure-selection-end hyper-structure))
  647.        (hyper-structure-selection-end hyper-structure)
  648.        (1+ (hyper-structure-selection-end hyper-structure))))
  649.     (set-mini-buffer mini-buffer-selected-message)))
  650.  
  651. ;;; Define this to highlight contained region when double clicked.
  652.  
  653. (defobfun (WINDOW-CLICK-EVENT-HANDLER *Hyper-Display-Window*) (where)
  654.   (declare (fixnum where)
  655.            (object-variable mini-buffer-selected-message 
  656.                             mini-buffer-unselected-message))
  657.   (if (double-click-p) 
  658.     (let ((hyper-structure (selected-hyper-structure)))
  659.       (set-mark (window-cursor-mark) 
  660.                 (hyper-structure-selection-start hyper-structure))
  661.       (set-selection-range 
  662.        ;; Special case guards against selecting off end of buffer.
  663.        (if (= (buffer-size (window-buffer))
  664.               (hyper-structure-selection-end hyper-structure))
  665.          (hyper-structure-selection-end hyper-structure)
  666.          (1+ (hyper-structure-selection-end hyper-structure))))
  667.       (set-mini-buffer mini-buffer-selected-message))
  668.     (progn 
  669.       (set-mini-buffer mini-buffer-unselected-message)
  670.       (funcall (ask *fred-window* (symbol-function 'window-click-event-handler))
  671.                where))))
  672.  
  673. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  674. ;;;
  675. ;;;                           EXPORTED FUNCTIONS
  676. ;;;
  677. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  678.  
  679. (defun CREATE-HYPER-DISPLAY (text-structure command-list &rest init-list)
  680.   "create-hyper-display <hyper-structure> <command-list> 
  681.                         &rest <keyword-args>
  682.                                           [*Hyper-Display-Window* Function]
  683.   Creates a Hyper-Display window with text and commands as specified.
  684.   The <keyword-args> can be any keyword/value pairs defined for Fred 
  685.   windows (eg. :window-size, :window-position, :window-title ...), 
  686.   plus the following additions:
  687.     :right-margin - fixnum indicating the maximum width of each line of
  688.       text generated from the <hyper-structure>.
  689.     :menu-message - string labeling the pop-up menu of commands.
  690.     :mini-buffer-selected-message
  691.     :mini-buffer-unselected-message - strings displayed in the mini-buffer
  692.       when there is and isn't a selection, respectively.  These should 
  693.       remind the user how the display is used.
  694.     :general-instructions - string displayed when user asks for help
  695.       when there is no selection.
  696.   The created window object is returned."
  697.   (check-type text-structure hyper-structure)
  698.   (check-type command-list list)
  699.   (assert (evenp (length init-list)) (init-list)
  700.           "Bad keyword/argument list (odd number of elements):~%~S" init-list)
  701.   (do ((init-ptr init-list (cddr init-ptr)))
  702.       ((null init-ptr))
  703.     (assert (typep (first init-ptr) 'keyword) (init-list)
  704.             "Non-keyword where keyword expected:~%~S" (first init-ptr)))
  705.   (apply #'oneof *hyper-display-window* 
  706.          :hyper-structure text-structure
  707.          :command-list   command-list
  708.          init-list))
  709.                         
  710. (defun DISPLAY-HYPER-STRUCTURE (text-structure hyper-display-window)
  711.   "display-hyper-structure <hyper-structure> <hyper-display-window>
  712.                                           [*Hyper-Display-Window* Function]
  713.   Changes the hyper-structure of the window to the indicated structure,
  714.   and displays the corresponding text."
  715.   (check-type text-structure hyper-structure)
  716.   (assert (typep hyper-display-window *hyper-display-window*)
  717.           (hyper-display-window) "~S is not a *hyper-display-window*")
  718.   (ask hyper-display-window 
  719.     (set-hyper-structure text-structure)
  720.     (window-update)))
  721.  
  722. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  723. (provide :Hyper-Display)
  724. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  725. ;;; The End.